import React, { Component } from "react"
import { BrowserRouter as Router, Route } from "react-router-dom"
import axios from "axios"
import "./App.css"
import "bootstrap/dist/css/bootstrap.min.css"

import Home from "./containers/Home"
import Create from "./containers/Create"

import { flattenArr, generateID, parseToYearAndMonth } from "./utility"

export const AppContext = React.createContext()

class App extends Component {
  constructor(props) {
    super(props)

    this.state = {
      items: {},
      categories: {},
      isLoading: false
    }

    const withLoading = cb => {
      return (...args) => {
        this.setState({
          isLoading: true
        })
        return cb(...args)
      }
    }

    this.actions = {
      getInitalData: withLoading(async () => {
        const currentDate = parseToYearAndMonth("2018-12-03")
        const itemsURL = `/items?monthCategory=${currentDate.year}-${
          currentDate.month
        }&_sort=timestamp&_order=desc`
        const promiseArr = [axios.get(itemsURL), axios.get("/categories")]
        const response = await Promise.all(promiseArr)
        const [items, categories] = response
        this.setState({
          items: flattenArr(items.data),
          categories: flattenArr(categories.data),
          isLoading: false
        })
      }),
      getCreateData: withLoading(async id => {
        const { items, categories } = this.state
        const promiseArr = []
        // 判断 state 中是否已经存在 categories，如果没有数据则执行异步获取
        if (Object.keys(categories).length === 0) {
          promiseArr[0] = axios.get("/categories")
        }
        const isItemFetched = !!items[id]
        // 当有 id 时（表示为编辑模式）并且 items 中没有相关 id 的数据，那么请求这个数据
        if (id && isItemFetched === false) {
          const ulrWithId = `/items/${id}`
          promiseArr[1] = axios.get(ulrWithId)
        }
        const [fetchCategories, editItem] = await Promise.all(promiseArr)
        // 判断是否有请求数据，从而选择使用正确的值
        const finalCategories = fetchCategories
          ? flattenArr(fetchCategories.data)
          : categories
        const finalItem = editItem ? editItem.data : items[id]
        // 更改组件状态
        if (id) {
          this.setState({
            categories: finalCategories,
            // 展开之前的 items，因为有可能是从主页面到编辑页面
            items: { ...items, [id]: finalItem },
            isLoading: false
          })
        } else {
          this.setState({
            categories: finalCategories,
            isLoading: false
          })
        }
        return {
          categories: finalCategories,
          editItem: finalItem
        }
      }),
      selectNewDate: withLoading(async (year, month) => {
        const itemsURL = `/items?monthCategory=${year}-${month}&_sort=timestamp&_order=desc`
        const res = await axios.get(itemsURL)
        this.setState({
          items: flattenArr(res.data),
          isLoading: false
        })
        return res
      }),
      deleteItem: withLoading(async item => {
        this.setState({
          isLoading: true
        })
        const res = await axios.delete(`/items/${item.id}`)
        const clone = { ...this.state.items }
        delete clone[item.id]
        this.setState({
          items: clone,
          isLoading: false
        })
        return res
      }),
      createItem: withLoading(async (data, cid) => {
        const newID = generateID()
        const parsedDate = parseToYearAndMonth(data.date)
        data.monthCategory = `${parsedDate.year}-${parsedDate.month}`
        data.timestamp = new Date(data.date).getTime()
        const newItem = { ...data, id: newID, cid }
        await axios.post("/items", newItem)
        this.setState({
          items: { ...this.state.items, [newID]: newItem },
          isLoading: false
        })
        return newItem
      }),
      updateItem: withLoading(async (data, cid, id) => {
        const parsedDate = parseToYearAndMonth(data.date)
        const item = {
          ...data,
          id,
          cid,
          timestamp: new Date(data.date).getTime(),
          monthCategory: `${parsedDate.year}-${parsedDate.month}`
        }
        await axios.put(`/items/${item.id}`, item)
        this.setState({
          items: { ...this.state.items, [item.id]: item },
          isLoading: false
        })
        return item
      })
    }
  }

  render() {
    return (
      <AppContext.Provider value={{ state: this.state, actions: this.actions }}>
        <Router>
          <div className="App container">
            <Route path="/" exact component={Home} />
            <Route path="/create" exact component={Create} />
            <Route path="/edit/:id" exact component={Create} />
          </div>
        </Router>
      </AppContext.Provider>
    )
  }
}

export default App
